

/** Matrix.cpp
 * File contains methods for the Matrix class
 * */

#include <cstdlib>
#include <cstring>
#include "../include/Matrix.h"

/* allocate space for a two dimensional array
 * for doubles only. */
static void allocSpace(double **ppd_array, int i_row, int i_col) {
	if (i_row <= 0 || i_col <= 0) {
		return;
	}
	ppd_array = (double**) malloc(sizeof(double*) * i_row);

	for (int i = 0; i < i_row; i++) {
		ppd_array[i] = (double*) malloc(sizeof(double) * i_col);
	}
}
/* free space for a two dimensional array
 * for doubles only. You need to parse the outest size
 * of the array. */
static void freeSpace(double **ppd_array, int i_row) {
	if (ppd_array == nullptr || i_row <= 0) {
		return;
	}
	for (int i = 0; i < i_row; i++) {
			FREENULL(ppd_array[i]);
	}
	FREENULL(ppd_array);
}

/* Constructors/Destructor */
/* creates the matrix with 1x1 dimension */
Matrix::Matrix() {
	i_column = 1;
	i_row = 1;
	allocSpace(ppd_matrix, 1, 1);
	memset(ppd_matrix, 0, sizeof(ppd_matrix)); /* as the memory is a row */
}
/* creates a param1 square matrix */
Matrix::Matrix(int i_dim) {
	if (i_dim > 0) {
		i_column = i_row = i_dim;
		allocSpace(ppd_matrix, i_row, i_column);
		memset(ppd_matrix, 0, sizeof(ppd_matrix));
	}
}
/* creates a param1 x param2 matrix */
Matrix::Matrix(int i_row, int i_col) {
	if (i_row > 0 && i_col > 0) {
		i_column = i_col;
		this->i_row = i_row;
		allocSpace(ppd_matrix, this->i_row, i_column);
		memset(ppd_matrix, 0, sizeof(ppd_matrix));
	}
}
/* creates a matrix with param1 as content */
Matrix::Matrix(double **ppd_matrix, int i_row, int i_col, bool free) {
	if (i_row > 0 && i_col > 0 && ppd_matrix != nullptr) {
		if ((sizeof(ppd_matrix) / sizeof(double**)) == (i_row * i_col)) {
			i_column = i_col;
			this->i_row = i_row;

			allocSpace(this->ppd_matrix, this->i_row, i_column);

			for (int i = 0; i < this->i_row; i++) {
				for (int j = 0; j < i_column; j++) {
					this->ppd_matrix[i][j] = ppd_matrix[i][j];
				}
			}

			if (free == true) {
				freeSpace(ppd_matrix, i_row);
			}
		}
	}
}
/* free memory, set pointers to nullptr */
Matrix::~Matrix() {
	freeSpace(ppd_matrix, i_row);
}

/* Operator Overload
 * Add to all values if dim(this) == dim(other) */
Matrix Matrix::operator+(const Matrix &other) {
	if (i_row == other.i_row || i_column != other.i_column) {
		return NULL;
	}
	if (i_row == 0 || i_column == 0) {
		return NULL;
	}
	Matrix result(i_row, i_column);
	for (int i = 0; i < i_row; i++) {
		for (int j = 0; j < i_column; j++) {
			double sum = ppd_matrix[i][j] + other.getMatrixValue(i, j);
			result.setMatrixValue(sum, i, j);
		}
	}

	return result;
}


/* Getter/Setter */
int Matrix::getRowCount() {
	return i_row;
}
int Matrix::getColumnCount() {
	return i_column;
}
double Matrix::getMatrixValue(int i_row, int i_col) {
	if (i_row < 0 || i_row >= this->i_row || i_col < 0 || i_column >= this->i_column) {
		return 0.;
	}

	return this->ppd_matrix[i_row][i_col];
}
void Matrix::setMatrixValue(double d_value, int i_row, int i_col)  {
	if (i_row >= this->i_row || i_col >= this->i_column) {
		return;
	}
	ppd_matrix[i_row][i_col] = d_value;
}
double **Matrix::getMatrix() {
	double **ppd_ret = nullptr;
	
	if (i_row <= 0 || i_column <= 0) {
		return ppd_ret;
	}

	allocSpace(ppd_ret, i_row, i_column);

	for (int i = 0; i < i_row; i++) {
		for (int j = 0; j < i_column; j++) {
			ppd_ret[i][j] = ppd_matrix[i][j];
		}
	}

	return ppd_ret;
}
double **Matrix::getMatrix(int i_row1, int i_row2, int i_col1, int i_col2) {
	double **ppd_ret = nullptr;
	int i_rowlen, i_collen;

	if (i_row <= 0 || i_column <= 0 || i_row1 <= 0 || i_row2 <= i_row1 || i_col1 <= 0 || i_col2 <= i_col1) {
		return ppd_ret;
	}
	i_rowlen = i_row2 - i_row1;
	i_collen = i_col2 - i_col1;

	allocSpace(ppd_ret, i_rowlen, i_collen);

	for (int i = i_row1; i < i_row2; i++) {
		for (int j = i_col1; j < i_col2; j++) {
			ppd_ret[i][j] = ppd_matrix[(i - i_row1)][(j - i_col1)];
		}
	}

	return ppd_ret;
}
long Matrix::getSize() {
	return (sizeof(double) * i_row * i_column);
}